%%%-------------------------------------------------------------------
%%% File    : pgt.erl
%%% Author  :  <vjache>
%%% Description : 
%%%
%%% Created : 16 Jan 2009 by  <vjache>
%%%-------------------------------------------------------------------
-module(pgt).

-include("../include/pgt.hrl").
-include("../include/util.hrl").

%% API
-export([
         new/1,
		 new_monitor/1,
         init/3,
         init_root/2,
         pg_spawn_worker/1,
         pg_spawn_worker/2,
         pg_spawn_worker/3,
         pg_spawn_worker/4,
         ppg_spawn_worker/1,
         ppg_spawn_worker/2,
         ppg_spawn_worker/3,
         ppg_spawn_worker/4,
         ppg_obtain_worker/1,
         ppg_obtain_worker/2,
         ppg_obtain_worker/3,
         ppg_obtain_worker/4,
         ppg_obtain_worker/5,
		 gppg_obtain_worker/2,
         wait_finish/0,
         wait_finish/1,
         worker_receive/2,
         worker_receive/3,
         worker_receive_fold/3,
         worker_receive_fold/4,
		 unwrap_error/1,
		 explain/1,
		 explainex/2,
		 test_prc/0
        ]).
-export([test/0]).
-define(SETUP_PARENTS(ParList),
		begin
			undefined=put(parents_info,ParList)
		end).
-define(PARENTS,
		begin
			get(parents_info)
		end).
-define(SETUP_PG_INFO(PgPid,ParentPgPid),
        begin
            put(parent_pg_pid,ParentPgPid),
            put(pg_pid,PgPid)
        end).
-define(EXIT_ON_PPG_SPAWN_DISABLED, 
		case ?IS_PPG_SPAWN_DISABLED of
			true -> exit(cant_spawn_workers_on_ppg);
			false -> ok
		end).
-define(I_AM_PGT_PROC(Info),put(i_am_worker_prc,Info)).
-define(AM_I_PGT_PROC,case get(i_am_worker_prc) of
						undefined ->
							false;
						_ ->
							true
		end).
-define(EXIT_IF_I_AM_NOT_PGT_PROC,case ?AM_I_PGT_PROC of
									true ->
										ok;
									false ->
										throw({not_a_pgt_proc,self()})
		end).
%%====================================================================
%% API
%%====================================================================

%%--------------------------------------------------------------------
%% Function: new(Pg) -> pid()
%% Description: Starts a tree of process groups using description.
%%				Each process group (PG) can spawn_link a worker 
%%				processes. Worker processes are alowed to ask its current 
%%				PG or parent PG to spawn_link worker processes. Worker 
%%				processes of PG are notified when child PG finishes its 
%%				existence.
%%--------------------------------------------------------------------
new(Pg) when is_record(Pg,pg)->
    spawn_link(?MODULE,init_root,[Pg,self()]).
new_monitor(Pg) when is_record(Pg,pg)->
    spawn_monitor(?MODULE,init_root,[Pg,self()]).
new_child(Pg,ParList) when is_record(Pg,pg)->
    spawn_link(?MODULE,init,[Pg,self(),ParList]).
%%--------------------------------------------------------------------
%% Function:	ppg_spawn_worker(WorkFun)
%%			 	ppg_spawn_worker(Node,WorkFun)
%%			 	ppg_spawn_worker(Module, Function, Args)
%%			 	ppg_spawn_worker(Node, Module, Function, Args) -> pid()
%% Description: Spawns a worker at parent process group. This fun is 
%% 				intended to be called in a worker process.
%%--------------------------------------------------------------------
ppg_spawn_worker(#worker{id=Id,spawn_spec=Spec})->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,Spec,Id);
ppg_spawn_worker(WorkFun) when is_function(WorkFun)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{WorkFun},make_ref()).
ppg_spawn_worker(Node,WorkFun)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{Node,WorkFun},make_ref()).
ppg_spawn_worker(Module, Function, Args)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{Module, Function, Args},make_ref()).
ppg_spawn_worker(Node, Module, Function, Args)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{Node, Module, Function, Args},make_ref()).
%%--------------------------------------------------------------------
%% Function:	ppg_obtain_worker(WorkId,WorkFun)
%%			 	ppg_obtain_worker(WorkId,Node,WorkFun)
%%			 	ppg_obtain_worker(WorkId,Module, Function, Args)
%%			 	ppg_obtain_worker(WorkId,Node, Module, Function, Args) -> pid()
%% Description: Spawns a worker at parent process group. This fun is 
%% 				intended to be called in a worker process.
%%--------------------------------------------------------------------
ppg_obtain_worker(WorkId,WorkFun) when is_function(WorkFun)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{WorkFun},WorkId).
ppg_obtain_worker(WorkId,Node,WorkFun)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{Node,WorkFun},WorkId).
ppg_obtain_worker(WorkId,Module, Function, Args)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{Module, Function, Args},WorkId).
ppg_obtain_worker(WorkId,Node, Module, Function, Args)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,{Node, Module, Function, Args},WorkId).
ppg_obtain_worker(#worker{id=Id,spawn_spec=Spec})->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
    spawn_dispatch(?PARENT_PG,Spec,Id).
gppg_obtain_worker(#worker{id=Id,spawn_spec=Spec},GppgId)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
	?EXIT_ON_PPG_SPAWN_DISABLED,
	case lists:keysearch(GppgId, 1, ?PARENTS) of
		{value,{GppgId,GppgPid}} ->
			spawn_dispatch(GppgPid,Spec,Id);
		false ->
			exit({no_such_grand_parent_group,GppgId,?PARENTS})
	end.
%%--------------------------------------------------------------------
%% Function:	pg_spawn_worker(WorkFun) 
%%			 	pg_spawn_worker(Node,WorkFun)
%%			 	pg_spawn_worker(Module, Function, Args)
%%			 	pg_spawn_worker(Node, Module, Function, Args) -> pid()
%% Description: Spawns a worker at current process group. This fun is 
%% 				intended to be called in a worker process.
%%--------------------------------------------------------------------
pg_spawn_worker(#worker{id=Id,spawn_spec=Spec})->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
    spawn_dispatch(?PG,Spec,Id);
pg_spawn_worker(WorkFun) when is_function(WorkFun)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
    spawn_dispatch(?PG,{WorkFun},make_ref()).
pg_spawn_worker(Node,WorkFun)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
    spawn_dispatch(?PG,{Node,WorkFun},make_ref()).
pg_spawn_worker(Module, Function, Args)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
    spawn_dispatch(?PG,{Module, Function, Args},make_ref()).
pg_spawn_worker(Node, Module, Function, Args)->
	?EXIT_IF_I_AM_NOT_PGT_PROC,
    spawn_dispatch(?PG,{Node, Module, Function, Args},make_ref()).
%%--------------------------------------------------------------------
%% Function: wait_finish() -> ok
%% Description: Block current process until 'finish' notification 
%% 				received from current process group.
%%--------------------------------------------------------------------
wait_finish() ->
    PgPid=?PG,
    receive
        {finish,PgPid} -> ok
%%     after 
%%             ?TIMEOUT -> exit({timeout,?TIMEOUT})
    end.
%%--------------------------------------------------------------------
%% Function: wait_finish() -> ok | timeout
%% Description: Block current process until 'finish' notification 
%% 				received from current process group or timeout occured.
%%--------------------------------------------------------------------
wait_finish(MillisToWait) ->
    PgPid=?PG,
    receive
        {finish,PgPid} -> ok
    after 
            MillisToWait -> timeout
    end.
%%--------------------------------------------------------------------
%% Function: worker_receive(OnMsgFun,OnFinishFun) -> term()
%% Description: This is a helper skeleton function to implement a 
%% 				worker process loop. It handles the finish 
%%				notification and timeout.
%%				OnMsgFun 	- OnMsgFun(Msg) -> term()
%%				OnFinishFun	- OnFinishFun()-> term()
%% Example:
%% 				loop(State)->
%%     				StateNew=pgt:worker_receive(
%%                		fun(Msg)->
%%                        	onMsg(Msg,State)
%%                		end,
%%                		fun()->{finish,State}end),
%%     				loop(StateNew) .
%%--------------------------------------------------------------------
worker_receive(OnMsgFun,OnFinishFun) ->
    worker_receive(OnMsgFun,
                   OnFinishFun,
                   fun(Time)-> 
                           exit({timeout,Time}) 
                   end).
%%--------------------------------------------------------------------
%% Function: worker_receive(OnMsgFun,OnFinishFun,TimeoutFun) -> term()
%% Description: This is a helper skeleton function to implement a 
%% 				worker process loop. It is a single 'receive' act that 
%%				handles the finish notification and timeout.
%%				OnMsgFun 	- OnMsgFun(Msg) -> term()
%%				OnFinishFun	- OnFinishFun() -> term()
%%				TimeoutFun 	- TimeoutFun(Timeout) -> term()
%% Example:
%% 				loop(State)->
%%     				StateNew=pgt:worker_receive(
%%                		fun(Msg)->
%%                        	onMsg(Msg,State)
%%                		end,
%%                		fun()->{finish,State}end),
%%     				loop(StateNew) .
%%--------------------------------------------------------------------
worker_receive(OnMsgFun,OnFinishFun,_TimeoutFun) ->
    PgPid=?PG,
    receive
		Msg when not is_tuple(Msg) orelse
				 size(Msg)/=2 orelse
				 element(1,Msg)/=finish
			  -> OnMsgFun(Msg)
	after 0 ->
			receive
				Msg when not is_tuple(Msg) orelse
				 		 size(Msg)/=2 orelse
				 		 element(1,Msg)/=finish
			  		-> OnMsgFun(Msg);
				{finish,PgPid} -> OnFinishFun()
%% 			after 
%%             	?TIMEOUT -> TimeoutFun(?TIMEOUT)
			end
    end.
%%--------------------------------------------------------------------
%% Function: worker_receive_fold(OnMsgFun,OnFinishFun,AccIn) -> Acc=term()
%% Description: This is a helper skeleton function to implement a 
%% 				worker process loop. It suggests more complete solution 
%%				than the worker_receive/2,3 skeleton. Actually it is a 
%%				ready worker process loop that must be fullfiled with 
%%				several callback functions.
%%				OnMsgFun 	- OnMsgFun(Msg,AccIn) -> AccOut = term()
%%				OnFinishFun	- OnFinishFun(AccIn) -> AccOut = term()
%%				AccIn		- term()
%% Example:
%% 				loop(State)->
%%     				pgt:worker_recive_fold(
%%                		fun(Msg,AccIn)->
%%                        	onMsg(Msg,AccIn)
%%                		end,
%%                		fun(AccIn)-> ok end,State).
%%--------------------------------------------------------------------
worker_receive_fold(_OnMsgFun,_OnFinishFun,{finish,AccFinal}) ->
    AccFinal;
worker_receive_fold(OnMsgFun,OnFinishFun,AccIn) ->
    AccOut=pgt:worker_receive(
             fun(Msg) ->
                     OnMsgFun(Msg,AccIn)
             end,
             fun() ->
                     AccFin=OnFinishFun(AccIn),{finish,AccFin} 
             end),
    worker_receive_fold(OnMsgFun,OnFinishFun,AccOut).
%%--------------------------------------------------------------------
%% Function: worker_receive_fold(OnMsgFun,OnFinishFun,AccIn) -> Acc=term()
%% Description: This is a helper skeleton function to implement a 
%% 				worker process loop. It suggests more complete solution 
%%				than the worker_receive/2,3 skeleton. Actually it is a 
%%				ready worker process loop that must be fullfiled with 
%%				several callback functions.
%%				OnMsgFun 	- OnMsgFun(Msg,AccIn) -> AccOut = term()
%%				OnFinishFun	- OnFinishFun(AccIn) -> AccOut = term()
%%				TimeoutFun 	- TimeoutFun(Timeout,AccIn) -> term()
%%				AccIn		- term()
%% Example:
%% 				loop(State)->
%%     				pgt:worker_recive_fold(
%%                		fun(Msg,AccIn)->
%%                        	onMsg(Msg,AccIn)
%%                		end,
%%                		fun()->{finish,State}end,State).
%%--------------------------------------------------------------------
worker_receive_fold(_OnMsgFun,_OnFinishFun,_TimeoutFun,{timeout,AccFinal}) ->
    AccFinal;
worker_receive_fold(_OnMsgFun,_OnFinishFun,_TimeoutFun,{finish,AccFinal}) ->
    AccFinal;
worker_receive_fold(OnMsgFun,OnFinishFun,TimeoutFun,AccIn) ->
    AccOut=pgt:worker_receive(
             fun(Msg) ->
                     OnMsgFun(Msg,AccIn)
             end,
             fun() ->
                     AccFin=OnFinishFun(AccIn),{finish,AccFin} 
             end,
             fun(Time) ->
                     AccFin=TimeoutFun(Time,AccIn),{timeout,AccFin} 
             end),
    worker_receive_fold(OnMsgFun,OnFinishFun,TimeoutFun,AccOut).

unwrap_error({work_fun_err,_Type,Error,at,_StackTrace}) ->
	unwrap_error(Error);
unwrap_error({linked_prc_fail,_SomePid,Reason}) ->
	unwrap_error(Reason);
unwrap_error(Reason) ->
	Reason.
%% loop(State)->
%%     StateNew=pgt:worker_receive(
%%                fun(Msg)->
%%                        onMsg(Msg,State)
%%                end,
%%                fun()->{finish,State}end),
%%     loop(StateNew) .
%% loop(State)->
%%     pgt:worker_recive_fold(
%%                fun(Msg,AccIn)->
%%                        onMsg(Msg,AccIn)
%%                end,
%%                fun()->{finish,State}end,State).

%%====================================================================
%% Internal functions
%%====================================================================
init_root(Pg,ParentPid) ->
	?ASSERT_ROOT,
	init(Pg,ParentPid,[]).
%%--------------------------------------------------------------------
%% Function: init(Pg,ParentPid) -> ok
%% Description: The body of a process group process.
%%--------------------------------------------------------------------
init(Pg,ParentPid,ParList)->
	?I_AM_PGT_PROC('PG'),
    ?SET_PRC_NAME(Pg#pg.name),
    ?SETUP_PG_INFO(self(),ParentPid),
	?SETUP_PARENTS(ParList),
    put(work_by_id,dict:new()),
    process_flag(trap_exit, true),
    ?LOG_TRACE("Start", []),
    ChildPgPids=[new_child(ChildPg,[{Pg#pg.id,self()}|ParList])||ChildPg<-Pg#pg.pgs],
    case Pg#pg.workers of
        null -> null;
		[] ->	null;
        SpawnWorkersFun when is_function(SpawnWorkersFun) -> 
            SpawnWorkersFun(); %% spawn initial workers if any
        WorkerList -> [pg_spawn_worker(WorkSpec)||WorkSpec <- WorkerList]
    end,
    loop(ChildPgPids),
    ?LOG_TRACE("Shut Down", []),
    ok.

loop(ChildPgPids) ->
    ParentPid=?PARENT_PG,
    receive
        {'EXIT',SomePid,normal} -> 
            ?LOG_TRACE("Linked prc ~p exited.", [SomePid]),
            case process_info(self(),links) of
                {links,[ParentPid]} -> %% No more linked processes except parent PG
                    del_worker(SomePid),
                    finish;
				{links,[]} -> %% No more linked processes
                    del_worker(SomePid),
                    finish;
                _ ->
                    del_worker(SomePid),%%Try clean worker PID from dict
                    ChildPgPids_new=link_down(
                                      ChildPgPids,SomePid),%%Try clean child PG PID from dict
                    loop(ChildPgPids_new)
            end;
        {'EXIT',SomePid,Reason} -> 
            ?LOG_TRACE("Linked prc ~p failed", [SomePid]),
            exit({linked_prc_fail,SomePid,Reason});
        {spawn_worker,ReplyPid,Work,WorkId} ->
            WorkerPid=obtain_worker(Work,WorkId),
            ReplyPid!{spawn_ok,WorkerPid},
            loop(ChildPgPids)
%%     after 
%%             ?TIMEOUT -> 
%%             ?LOG_TRACE("Timed out ~p", [?TIMEOUT]),
%%             exit({timeout,?TIMEOUT})
    end.

spawn_dispatch(PgPid,Work,WorkId)->
	SelfPid=self(),
	case PgPid of
		SelfPid -> obtain_worker(Work,WorkId);
		_ ->
			CacheId={cached_worker,PgPid,Work,WorkId},
			case get(CacheId) of
				undefined ->	
					PgPid!{spawn_worker,SelfPid,Work,WorkId},
					receive
						{spawn_ok,WorkerPid} ->
							undefined=put(CacheId,WorkerPid),
							WorkerPid
					end;
				WorkerPid ->
					WorkerPid
			end
	end.

get_worker(WorkId) ->
    case dict:find(WorkId,get(work_by_id)) of
        {ok,WorkInfo} -> WorkInfo;
        error -> notfound
    end.

put_worker(WorkId,WorkPid) ->
    reg_worker(WorkId,WorkPid,alive).

reg_worker(WorkId,WorkPid,Status) ->
    WorkInfo={WorkPid,Status},
    put(work_by_id,dict:store(WorkId,WorkInfo,get(work_by_id))),
    WorkInfo.

del_worker(WorkPid) ->
    Dbg=[{K,V}||{K,{Pid,_}=V}<-dict:to_list(get(work_by_id)),Pid==WorkPid],
    ?LOG_TRACE("in del_worker ~p",[Dbg]),
	case Dbg of
		[] -> noop;
		[{_WorkId,{WorkPid,dead}}=E] -> exit({del_already_dead_worker,E});
		[{WorkId,{WorkPid,alive}}=_E] -> 
            ?LOG_TRACE("become dead ~p",[_E]),
            reg_worker(WorkId,WorkPid,dead)
	end.

spawn_link_worker(Work,WorkId)->
    PgPid=?PG,
    ParentPgPid=?PARENT_PG,
	PPGSpawnDisable=?AM_I_ROOT,
	Parents=?PARENTS,
	WorkerSetupFun=fun() ->
						   case PPGSpawnDisable of
							   true ->
								   ?PPG_SPAWN_DISABLE;
							   false ->
								   ok
						   end,
						   ?SET_PRC_NAME(WorkId),
						   ?SETUP_PG_INFO(PgPid,ParentPgPid),
						   ?I_AM_PGT_PROC(_Info={WorkId}),
						   ?SETUP_PARENTS(Parents)
				   end,
    case Work of
        {WorkFun} ->
            spawn_link(fun()->
							   WorkerSetupFun(),
                               apply_work_fun(WorkFun) 
                       end);
        {Node,WorkFun} -> 
            spawn_link(Node,
                       fun()->
							   WorkerSetupFun(),
                               apply_work_fun(WorkFun) 
                       end);
        {Module, Function, Args} -> 
            spawn_link(fun()->
							   WorkerSetupFun(),
                               apply_work_fun({Module, Function, Args}) 
                       end);
        {Node, Module, Function, Args} -> 
            spawn_link(Node,
                       fun()->
							   WorkerSetupFun(),
                               apply_work_fun({Module, Function, Args})
                       end)
    end.

apply_work_fun(WorkFun) when is_function(WorkFun, 0) ->
	try WorkFun() of
		_ ->
			ok
	catch
		Type:Error ->
			exit({work_fun_err,Type,Error,at,erlang:get_stacktrace()})
	end;
apply_work_fun({Module, Function, Args}) ->
	try apply(Module, Function, Args) of
		_ ->
			ok
	catch
		Type:Error ->
			exit({work_fun_err,Type,Error,at,erlang:get_stacktrace()})
	end.

	
obtain_worker(Work,WorkId) ->
    case get_worker(WorkId) of
        {WorkPid,alive} ->
			?LOG_TRACE("Reuse worker PID:~p ID:~p", [WorkPid,WorkId]),
			WorkPid;
        {WorkPid,dead} -> exit({obtain_already_dead_worker,{WorkId,WorkPid}});
        notfound -> 
            {WorkPid,alive}=put_worker(WorkId, 
                                       spawn_link_worker(Work,WorkId)),
            ?LOG_TRACE("Spawned worker PID:~p ID:~p", [WorkPid,WorkId]),
            WorkPid
    end.
    

link_down([],_SomePid)->
    [];
link_down(ChildPgPids,SomePid)->
    ChildPgPids_new=lists:delete(SomePid, ChildPgPids),
    if 
        ChildPgPids_new==[]-> 
            fire_finish(),
            [];
        true -> ChildPgPids_new
    end.

fire_finish() ->
    ParentPid=?PARENT_PG,
    {links,Pids}=process_info(self(),links),
    [WorkerPid!{finish,self()}||WorkerPid<-Pids,WorkerPid/=ParentPid].


-define(TAB,"   ").

explain(Pg) ->
	explainex(Pg,fun(_Tab,_SpawnSpec)->
						 "ok"
			  end).
explainex(Pg,SpawnSpecExplFun) ->
	explain(Pg,"",SpawnSpecExplFun).

explain(Pg,Tab,SpawnSpecExplFun) when is_record(Pg,pg) ->
	[io_lib:format(Tab++"pg.name=~p~n",[Pg#pg.name]),
	io_lib:format(Tab++"pg.id=~p~n",[Pg#pg.id]),
	io_lib:format(Tab++"pg.pgs=[~n",[]),
	[explain(ChPg,Tab++?TAB,SpawnSpecExplFun)||ChPg<-Pg#pg.pgs],
	io_lib:format(Tab++"]~n",[]),
	case Pg#pg.workers of
		null ->
			[];
		Workes ->
			[io_lib:format(Tab++"pg.workers=[~n",[]),
			[explain(Worker,Tab++?TAB,SpawnSpecExplFun)||Worker<-Workes]]
	end,
	io_lib:format(Tab++"]~n",[])];
explain(Worker,Tab,SpawnSpecExplFun) when is_record(Worker,worker) ->
	[io_lib:format(Tab++"worker.id=~p~n",[Worker#worker.id]),
	io_lib:format(Tab++"worker.spawn_spec=[~n",[]),
	SpawnSpecExplFun(Tab++?TAB,Worker#worker.spawn_spec),
	io_lib:format(Tab++"]~n",[])].
%Worker#worker.spawn_spec]).

	
%%%%%%%%%%%%%%%%%%%%%%%%%%%%% Tests %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%-record(pg,{id,nested_pgs,init_fun=null})
test() ->
    Pg=#pg{pgs=[
                #pg{workers=
                        fun()->
                                pgt:pg_spawn_worker(
                                  fun()->
                                          test_start()
                                  end)
                        end}
               ]},
    new(Pg).
test_start()->
    ?SET_PRC_NAME("Leaf Worker"),
    _WrkPid=pgt:ppg_obtain_worker("My Special Worker",fun()->
                                        %%?SET_PRC_NAME("Dependent Worker"),
                                        test_loop(3),
                                        ?LOG_TRACE("start wait finish", []),
                                        %%exit(test_failure),
                                        pgt:wait_finish(),
                                        ?LOG_TRACE("finish received", [])
                                end),
    pgt:ppg_obtain_worker("My Special Worker1",fun()->
                                        %%?SET_PRC_NAME("Dependent Worker"),
                                        test_loop(3),
                                        ?LOG_TRACE("start wait finish", []),
                                        %%exit(test_failure),
                                        pgt:wait_finish(),
                                        ?LOG_TRACE("finish received", [])
                                end),
    ?LOG_TRACE("I spawned worker ~p at parent pg", [WrkPid]),
    test_loop(10).
test_loop(0)->
    ?LOG_TRACE("done", []);
test_loop(N)->
    receive
        _ -> ok
    after 10 -> 
            ?LOG_TRACE("tick", []),
            test_loop(N-1)
    end.

test_prc() ->
	spawn_link(fun()->
					   
	receive
		Msg when not is_tuple(Msg) orelse
				 size(Msg)/=2 orelse
				 element(1,Msg)/=finish ->
			io:format(">>~p~n",[Msg]);
        {finish,_PgPid} -> io:format(">>FINISH~n")
    end
			   end).
